package me.ccrama.redditslide.Views; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.webkit.DownloadListener; import android.webkit.WebChromeClient; import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.RelativeLayout; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener; import org.apache.commons.lang3.StringEscapeUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import me.ccrama.redditslide.Adapters.ImageGridAdapter; import me.ccrama.redditslide.ContentType; import me.ccrama.redditslide.ForceTouch.PeekViewActivity; import me.ccrama.redditslide.ImgurAlbum.AlbumUtils; import me.ccrama.redditslide.ImgurAlbum.Image; import me.ccrama.redditslide.R; import me.ccrama.redditslide.Reddit; import me.ccrama.redditslide.SecretConstants; import me.ccrama.redditslide.SettingValues; import me.ccrama.redditslide.Tumblr.Photo; import me.ccrama.redditslide.Tumblr.TumblrUtils; import me.ccrama.redditslide.util.AdBlocker; import me.ccrama.redditslide.util.GifUtils; import me.ccrama.redditslide.util.HttpUtil; import me.ccrama.redditslide.util.LogUtil; import me.ccrama.redditslide.util.NetworkUtil; /** * Created by ccrama on 3/5/2015. */ public class PeekMediaView extends RelativeLayout { ContentType.Type contentType; private GifUtils.AsyncLoadGif gif; private MediaVideoView videoView; public WebView website; private ProgressBar progress; private SubsamplingScaleImageView image; public PeekMediaView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public PeekMediaView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public PeekMediaView(Context context) { super(context); init(); } boolean web; float origY = 0; public void doClose() { website.setVisibility(View.GONE); website.loadUrl("about:blank"); videoView.stopPlayback(); if (gif != null) gif.cancel(true); } public void doScroll(MotionEvent event) { if (origY == 0) { origY = event.getY(); } if (web && website.canScrollVertically((origY - event.getY()) > 0 ? 0 : 1) && Math.abs(origY - event.getY()) > website.getHeight() / 4) { website.scrollBy(0, (int) -(origY - event.getY()) / 5); } } public void setUrl(String url) { contentType = ContentType.getContentType(url); switch (contentType) { case ALBUM: doLoadAlbum(url); progress.setIndeterminate(true); break; case TUMBLR: doLoadTumblr(url); progress.setIndeterminate(true); break; case EMBEDDED: case EXTERNAL: case LINK: case VIDEO: case SELF: case SPOILER: case NONE: doLoadLink(url); progress.setIndeterminate(false); break; case REDDIT: progress.setIndeterminate(true); doLoadReddit(url); break; case DEVIANTART: doLoadDeviantArt(url); progress.setIndeterminate(false); break; case IMAGE: doLoadImage(url); progress.setIndeterminate(false); break; case XKCD: doLoadXKCD(url); progress.setIndeterminate(false); break; case IMGUR: doLoadImgur(url); progress.setIndeterminate(false); break; case GIF: case STREAMABLE: case VID_ME: doLoadGif(url); progress.setIndeterminate(false); break; } } private void doLoadAlbum(final String url) { new AlbumUtils.GetAlbumWithCallback(url, (PeekViewActivity) getContext()) { @Override public void onError() { ((PeekViewActivity) getContext()).runOnUiThread(new Runnable() { @Override public void run() { doLoadLink(url); } }); } @Override public void doWithData(final List<Image> jsonElements) { super.doWithData(jsonElements); progress.setVisibility(View.GONE); images = new ArrayList<>(jsonElements); displayImage(images.get(0).getImageUrl()); if (images.size() > 1) { GridView grid = (GridView) findViewById(R.id.grid_area); grid.setNumColumns(5); grid.setVisibility(VISIBLE); grid.setAdapter(new ImageGridAdapter(getContext(), images)); } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void doLoadTumblr(final String url) { new TumblrUtils.GetTumblrPostWithCallback(url, (PeekViewActivity) getContext()) { @Override public void onError() { ((PeekViewActivity) getContext()).runOnUiThread(new Runnable() { @Override public void run() { doLoadLink(url); } }); } @Override public void doWithData(final List<Photo> jsonElements) { super.doWithData(jsonElements); progress.setVisibility(View.GONE); tumblrImages = new ArrayList<>(jsonElements); displayImage(tumblrImages.get(0).getOriginalSize().getUrl()); if (tumblrImages.size() > 1) { GridView grid = (GridView) findViewById(R.id.grid_area); grid.setNumColumns(5); grid.setVisibility(VISIBLE); grid.setAdapter(new ImageGridAdapter(getContext(), tumblrImages, true)); } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } List<Image> images; List<Photo> tumblrImages; WebChromeClient client; WebViewClient webClient; public void setValue(int newProgress) { progress.setProgress(newProgress); if (newProgress == 100) { progress.setVisibility(View.GONE); } else if (progress.getVisibility() == View.GONE) { progress.setVisibility(View.VISIBLE); } } private class MyWebViewClient extends WebChromeClient { @Override public void onProgressChanged(WebView view, int newProgress) { setValue(newProgress); super.onProgressChanged(view, newProgress); } } public void doLoadXKCD(final String url) { if (NetworkUtil.isConnected(getContext())) { final String apiUrl = (url.endsWith("/") ? url : (url + "/")) + "info.0.json"; new AsyncTask<Void, Void, JsonObject>() { @Override protected JsonObject doInBackground(Void... params) { return HttpUtil.getJsonObject(Reddit.client, new Gson(), apiUrl); } @Override protected void onPostExecute(final JsonObject result) { if (result != null && !result.isJsonNull() && result.has("error")) { doLoadLink(url); } else { try { if (result != null && !result.isJsonNull() && result.has("img")) { doLoadImage(result.get("img").getAsString()); } else { doLoadLink(url); } } catch (Exception e2) { doLoadLink(url); } } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } public void doLoadLink(String url) { client = new MyWebViewClient(); web = true; webClient = new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { website.loadUrl( "javascript:(function() { document.getElementsByTagName('video')[0].play(); })()"); } private Map<String, Boolean> loadedUrls = new HashMap<>(); @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { boolean ad; if (!loadedUrls.containsKey(url)) { ad = AdBlocker.isAd(url, getContext()); loadedUrls.put(url, ad); } else { ad = loadedUrls.get(url); } return ad && SettingValues.tabletUI ? AdBlocker.createEmptyResource() : super.shouldInterceptRequest(view, url); } }; website.setVisibility(View.VISIBLE); website.setWebChromeClient(client); website.setWebViewClient(webClient); website.getSettings().setBuiltInZoomControls(true); website.getSettings().setDisplayZoomControls(false); website.getSettings().setJavaScriptEnabled(true); website.getSettings().setLoadWithOverviewMode(true); website.getSettings().setUseWideViewPort(true); website.setDownloadListener(new DownloadListener() { public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { //Downloads using download manager on default browser Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(url)); getContext().startActivity(i); } }); website.loadUrl(url); } private void doLoadReddit(String url) { RedditItemView v = (RedditItemView) findViewById(R.id.reddit_item); v.loadUrl(this, url, progress); } public void doLoadDeviantArt(String url) { final String apiUrl = "http://backend.deviantart.com/oembed?url=" + url; LogUtil.v(apiUrl); new AsyncTask<Void, Void, JsonObject>() { @Override protected JsonObject doInBackground(Void... params) { return HttpUtil.getJsonObject(Reddit.client, new Gson(), apiUrl); } @Override protected void onPostExecute(JsonObject result) { LogUtil.v("doLoad onPostExecute() called with: " + "result = [" + result + "]"); if (result != null && !result.isJsonNull() && (result.has("fullsize_url") || result.has("url"))) { String url; if (result.has("fullsize_url")) { url = result.get("fullsize_url").getAsString(); } else { url = result.get("url").getAsString(); } doLoadImage(url); } else { //todo error out } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } public void doLoadImgur(String url) { if (url.endsWith("/")) { url = url.substring(0, url.length() - 1); } final String finalUrl = url; String hash = url.substring(url.lastIndexOf("/"), url.length()); if (NetworkUtil.isConnected(getContext())) { if (hash.startsWith("/")) hash = hash.substring(1, hash.length()); final String apiUrl = "https://imgur-apiv3.p.mashape.com/3/image/" + hash + ".json"; LogUtil.v(apiUrl); new AsyncTask<Void, Void, JsonObject>() { @Override protected JsonObject doInBackground(Void... params) { return HttpUtil.getImgurMashapeJsonObject(Reddit.client, new Gson(), apiUrl, SecretConstants.getImgurApiKey(getContext())); } @Override protected void onPostExecute(JsonObject result) { if (result != null && !result.isJsonNull() && result.has("error")) { ///todo error out } else { try { if (result != null && !result.isJsonNull() && result.has("image")) { String type = result.get("image") .getAsJsonObject() .get("image") .getAsJsonObject() .get("type") .getAsString(); String urls = result.get("image") .getAsJsonObject() .get("links") .getAsJsonObject() .get("original") .getAsString(); if (type.contains("gif")) { doLoadGif(urls); } else if (!imageShown) { //only load if there is no image displayImage(urls); } } else if (result != null && result.has("data")) { String type = result.get("data") .getAsJsonObject() .get("type") .getAsString(); String urls = result.get("data") .getAsJsonObject() .get("link") .getAsString(); String mp4 = ""; if (result.get("data").getAsJsonObject().has("mp4")) { mp4 = result.get("data") .getAsJsonObject() .get("mp4") .getAsString(); } if (type.contains("gif")) { doLoadGif(((mp4 == null || mp4.isEmpty()) ? urls : mp4)); } else if (!imageShown) { //only load if there is no image displayImage(urls); } } else { if (!imageShown) doLoadImage(finalUrl); } } catch (Exception e2) { //todo error out } } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } boolean imageShown; public void doLoadImage(String contentUrl) { if (contentUrl != null && contentUrl.contains("bildgur.de")) { contentUrl = contentUrl.replace("b.bildgur.de", "i.imgur.com"); } if (contentUrl != null && ContentType.isImgurLink(contentUrl)) { contentUrl = contentUrl + ".png"; } if (contentUrl != null && contentUrl.contains("m.imgur.com")) { contentUrl = contentUrl.replace("m.imgur.com", "i.imgur.com"); } if (contentUrl == null) { //todo error out } if ((contentUrl != null && !contentUrl.startsWith("https://i.redditmedia.com") && !contentUrl.startsWith("https://i.reddituploads.com") && !contentUrl.contains( "imgur.com"))) { //we can assume redditmedia and imgur links are to direct images and not websites progress.setVisibility(View.VISIBLE); progress.setIndeterminate(true); final String finalUrl2 = contentUrl; new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { URL obj = new URL(finalUrl2); URLConnection conn = obj.openConnection(); final String type = conn.getHeaderField("Content-Type"); ((PeekViewActivity) getContext()).runOnUiThread(new Runnable() { @Override public void run() { if (!imageShown && type != null && !type.isEmpty() && type.startsWith("image/")) { //is image if (type.contains("gif")) { doLoadGif(finalUrl2.replace(".jpg", ".gif") .replace(".png", ".gif")); } else if (!imageShown) { displayImage(finalUrl2); } actuallyLoaded = finalUrl2; } else if (!imageShown) { //todo error out } } }); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { progress.setVisibility(View.GONE); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { displayImage(contentUrl); } } String actuallyLoaded; public void doLoadGif(final String dat) { videoView = (MediaVideoView) findViewById(R.id.gif); videoView.clearFocus(); findViewById(R.id.gifarea).setVisibility(View.VISIBLE); findViewById(R.id.submission_image).setVisibility(View.GONE); progress.setVisibility(View.VISIBLE); gif = new GifUtils.AsyncLoadGif((PeekViewActivity) getContext(), (MediaVideoView) findViewById(R.id.gif), progress, null, false, true, true, "") { @Override public void onError() { doLoadLink(dat); } }; gif.execute(dat); } public void displayImage(final String urlB) { LogUtil.v("Displaying " + urlB); final String url = StringEscapeUtils.unescapeHtml4(urlB); if (!imageShown) { actuallyLoaded = url; final SubsamplingScaleImageView i = (SubsamplingScaleImageView) findViewById(R.id.submission_image); i.setMinimumDpi(70); i.setMinimumTileDpi(240); progress.setIndeterminate(false); progress.setProgress(0); final Handler handler = new Handler(); final Runnable progressBarDelayRunner = new Runnable() { public void run() { progress.setVisibility(View.VISIBLE); } }; handler.postDelayed(progressBarDelayRunner, 500); ImageView fakeImage = new ImageView(getContext()); fakeImage.setLayoutParams(new LinearLayout.LayoutParams(i.getWidth(), i.getHeight())); fakeImage.setScaleType(ImageView.ScaleType.CENTER_CROP); File f = ((Reddit) getContext().getApplicationContext()).getImageLoader() .getDiscCache() .get(url); if (f != null && f.exists()) { imageShown = true; i.setOnImageEventListener(new SubsamplingScaleImageView.OnImageEventListener() { @Override public void onReady() { } @Override public void onImageLoaded() { } @Override public void onPreviewLoadError(Exception e) { } @Override public void onImageLoadError(Exception e) { imageShown = false; LogUtil.v("No image displayed"); } @Override public void onTileLoadError(Exception e) { } }); try { i.setImage(ImageSource.uri(f.getAbsolutePath())); i.setZoomEnabled(false); } catch (Exception e) { imageShown = false; //todo i.setImage(ImageSource.bitmap(loadedImage)); } (progress).setVisibility(View.GONE); handler.removeCallbacks(progressBarDelayRunner); } else { ((Reddit) getContext().getApplicationContext()).getImageLoader() .displayImage(url, new ImageViewAware(fakeImage), new DisplayImageOptions.Builder().resetViewBeforeLoading(true) .cacheOnDisk(true) .imageScaleType(ImageScaleType.NONE) .cacheInMemory(false) .build(), new ImageLoadingListener() { private View mView; @Override public void onLoadingStarted(String imageUri, View view) { imageShown = true; mView = view; } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { Log.v(LogUtil.getTag(), "LOADING FAILED"); imageShown = false; } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { imageShown = true; File f = ((Reddit) getContext().getApplicationContext()).getImageLoader() .getDiscCache() .get(url); if (f != null && f.exists()) { i.setImage(ImageSource.uri(f.getAbsolutePath())); } else { i.setImage(ImageSource.bitmap(loadedImage)); } (progress).setVisibility(View.GONE); handler.removeCallbacks(progressBarDelayRunner); } @Override public void onLoadingCancelled(String imageUri, View view) { Log.v(LogUtil.getTag(), "LOADING CANCELLED"); } }, new ImageLoadingProgressListener() { @Override public void onProgressUpdate(String imageUri, View view, int current, int total) { progress.setProgress(Math.round(100.0f * current / total)); } }); } } } private void init() { inflate(getContext(), R.layout.peek_media_view, this); this.image = (SubsamplingScaleImageView) findViewById(R.id.submission_image); this.videoView = (MediaVideoView) findViewById(R.id.gif); this.website = (WebView) findViewById(R.id.website); this.progress = ((ProgressBar) findViewById(R.id.progress)); } }